#include "Json.hpp"
using namespace ym;
size_t Json::alive_count = 0;
Json::Json() : jsonType(json_null)
{
    this->value.val_bool = false;
    Json::alive_count++;
}

Json::Json(bool val) : jsonType(json_bool)
{
    this->value.val_bool = val;
    Json::alive_count++;
}

Json::Json(int val) : jsonType(json_int)
{
    this->value.val_int = val;
    Json::alive_count++;
}

Json::Json(double val) : jsonType(json_double)
{
    this->value.val_double = val;
    Json::alive_count++;
}

Json::Json(const std::string &val) : jsonType(json_string)
{
    this->value.val_string = new std::string(val.c_str());
    Json::alive_count++;
}

Json::Json(Array *val) : jsonType(json_array)
{
    this->value.val_array = val;
    Json::alive_count++;
}

Json::Json(Object *val) : jsonType(json_object)
{
    this->value.val_object = val;
    Json::alive_count++;
}

Json::~Json()
{
    if (jsonType == json_string)
    {
        delete value.val_string;
    }
    if (jsonType == json_array)
    {
        Array *array = value.val_array;
        for (auto it = array->begin(); it != array->end();)
        {
            delete ((Json *)*it);
            it = array->erase(it);
        }
        delete value.val_array;
    }
    else if (jsonType == json_object)
    {
        Object *object = value.val_object;
        for (auto it = object->begin(); it != object->end();)
        {
            delete it->second;
            it = object->erase(it);
        }
        delete value.val_object;
    }
    Json::alive_count--;
#ifdef DEBUG
    std::cout << "Json-" << getTypeName() << ":\tfree\talivecount:\t" << Json::alive_count << std::endl;
#endif
}
int ym::Json::getInt()
{
    if (jsonType != json_int)
    {
        throw std::runtime_error("Json:\tnot int");
    }
    return value.val_int;
}

double ym::Json::getDouble()
{
    if (jsonType == json_double)
    {
        return value.val_double;
    }
    else if (jsonType == json_int)
    {
        return (double)value.val_int;
    }
    else
    {
        throw std::runtime_error("Json:\tnot double");
    }
}

bool ym::Json::getBool()
{
    if (jsonType != json_bool)
    {
        throw std::runtime_error("Json:\not bool");
    }
    return value.val_bool;
}

const std::string &ym::Json::getString()
{
    if (jsonType != json_string)
    {
        throw std::runtime_error("Json:\tnot string");
    }
    return *value.val_string;
}

const ym::Json::Array &ym::Json::getArray()
{
    if (jsonType != json_array)
    {
        throw std::runtime_error("Json:\tnot array");
    }
    return *value.val_array;
}

const ym::Json::Object &ym::Json::getObject()
{
    if (jsonType != json_object)
    {
        throw std::runtime_error("Json:\tnot object");
    }
    return *value.val_object;
}

bool Json::isBoolean()
{
    return jsonType == json_bool;
}

bool Json::isNull()
{
    return jsonType == json_null;
}

bool Json::isInt()
{
    return jsonType == json_int;
}

bool Json::isDouble()
{
    return jsonType == json_double;
}

bool Json::isString()
{
    return jsonType == json_string;
}

bool Json::isArray()
{
    return jsonType == json_array;
}

bool Json::isObject()
{
    return jsonType == json_object;
}

bool Json::isEmpty()
{
    if (jsonType != json_array && jsonType != json_object)
    {
        throw std::runtime_error("Json:\tnot array or object");
    }
    if (jsonType == json_array)
    {
        return value.val_array->empty();
    }
    else
    {
        return value.val_object->empty();
    }
}

bool Json::hasKey(const std::string &key)
{
    if (jsonType != json_object)
    {
        throw std::runtime_error("Json:\tnot object");
    }
    return value.val_object->find(key) != value.val_object->end();
}

Json *Json::get(int index)
{
    if (jsonType != json_array)
    {
        throw std::runtime_error("Json:\tnot array");
    }
    return value.val_array->at(index);
}

Json *Json::get(const std::string &key)
{
    if (jsonType != json_object)
    {
        throw std::runtime_error("Json:\tnot object");
    }
    try
    {
        return value.val_object->at(key);
    }
    catch (std::exception &err)
    {
        throw std::runtime_error(std::string("Object:\tnot find the key \'").append(key).append("\'"));
    }
}
std::string ym::Json::toString()
{
    std::string str;
    switch (jsonType)
    {
    case Json::json_null:
        str += "null";
        break;
    case Json::json_bool:
        str += (value.val_bool == true ? "true" : "false");
        break;
    case Json::json_int:
        str += std::to_string(value.val_int);
        break;
    case Json::json_double:
        str += std::to_string(value.val_double);
        break;
    case Json::json_string:
    {
        str.append("\"").append(*value.val_string).append("\"");
    }
    break;
    case Json::json_array:
        str += "[";
        for (auto i = value.val_array->begin(); i < value.val_array->end(); i++)
        {
            if (i != value.val_array->begin())
                str += ",";
            str += (*i)->toString();
        }
        str += "]";
        break;
    case Json::json_object:
        str += "{";
        for (auto i = value.val_object->begin(); i != value.val_object->end(); i++)
        {
            if (i != value.val_object->begin())
            {
                str += ",";
            }
            str.append("\"").append(i->first).append("\":").append(i->second->toString());
        }
        str += "}";
        break;
    default:
        break;
    }
    return str;
}

const char *ym::Json::getTypeName()
{
    switch (jsonType)
    {
    case json_null:
        return "null";
    case json_bool:
        return "bool";
    case json_int:
        return "int";
    case json_double:
        return "double";
    case json_string:
        return "string";
    case json_array:
        return "array";
    case json_object:
        return "object";
    default:
        return "unknown";
        break;
    }
}

std::ostream &ym::operator<<(std::ostream &os, const Json &json)
{
    switch (json.jsonType)
    {
    case Json::json_null:
        os << "null";
        break;
    case Json::json_bool:
        os << (json.value.val_bool == true ? "true" : "false");
        break;
    case Json::json_int:
        os << json.value.val_int;
        break;
    case Json::json_double:
        os << json.value.val_double;
        break;
    case Json::json_string:
    {
        os << '"' << *json.value.val_string << '"';
    }
    break;
    case Json::json_array:
        os << "[";
        for (auto i = json.value.val_array->begin(); i < json.value.val_array->end(); i++)
        {
            if (i != json.value.val_array->begin())
                os << ",";
            os << **i;
        }
        os << "]";
        break;
    case Json::json_object:
        os << "{";
        for (auto i = json.value.val_object->begin(); i != json.value.val_object->end(); i++)
        {
            if (i != json.value.val_object->begin())
            {
                os << ",";
            }
            os << "\"" << i->first << "\":" << *i->second;
        }
        os << "}";
        break;
    default:
        break;
    }
    return os;
}
